﻿#include <QStringList>
#include <QDebug>
#include <fstream>
#include "translator.h"
#include "sentence.h"
#include "literals/delta.h"
#include "literals/operator.h"
#include "literals/bracket.h"
#include "literals/variable.h"

Translator::Translator()
  {
  setDefaultRegisteredNames();
  }
void Translator::registerName(QString sName, Word::Type type)  
  {
  _registeredNames.insert(sName, type);
  }
int Translator::clearRegisteredNames()
  {
  int count = _registeredNames.count();
  _registeredNames.clear();
  return count;
  }
void Translator::setDefaultRegisteredNames()
  {
  _registeredNames.insert("a", Word::WT_OPERATOR);
  _registeredNames.insert("b", Word::WT_OPERATOR);
  _registeredNames.insert("c", Word::WT_OPERATOR);
  _registeredNames.insert("d", Word::WT_OPERATOR);
  _registeredNames.insert("delta", Word::WT_DELTA);
  }

QString Translator::sentenceToString(const Sentence &sen)
  {
  Expression e;
  e.push_back(sen);
  QString ret = expToString(e);
  return ret;
  }

QString Translator::expToString(Expression const &exp)
  {
  if (0 == exp.size())
    return "0";
  QString sExp;
  sExp.reserve(20);
  for (size_t i = 0; i < exp.size(); ++i)
    {
    const Sentence &s = exp.at(i); // this is only for code simplification later on
    QString sCoeff;
    if (s.size() > 0)
      {
      if (s.getCoefficient() == 0)
        continue;
      if (s.getCoefficient() > 0)
        {
        if ((s.getCoefficient() == 1) && (i != 0)) // there is no need to start the exression with "+"
          sCoeff = "+ ";
        else if ((s.getCoefficient() != 1) && (i == 0))
          sCoeff = QString::number(s.getCoefficient()) + " ";
        else if ((s.getCoefficient() != 1))
          sCoeff = "+ " + QString::number(s.getCoefficient()) + " ";
        }
      else // coeff < 0
        {
        if (s.getCoefficient() == -1)
          {
          sCoeff = "-";
          if (i != 0)
            sCoeff += " ";
          }
        else
          {
          sCoeff += "-";
          if (i != 0)
            sCoeff += " ";
          sCoeff += QString::number(s.getCoefficient() * -1) + " ";
          }
        }
      }
    else // s.size() == 0; this means we have a standalone number
      {
      if (s.getCoefficient() > 0)
        {
        if (i != 0)
          sCoeff = "+ " + QString::number(s.getCoefficient()) + " ";
        else
          sCoeff = QString::number(s.getCoefficient()) + " ";
        }
      else if (s.getCoefficient() < 0)
        {
        sCoeff += "-";
        if (i != 0)
          sCoeff += " ";
        sCoeff += QString::number(s.getCoefficient() * -1) + " ";
        }
      }

    sExp += sCoeff;
    int nWordsCount = s.size();

    for (int j = 0; j < nWordsCount; ++j)
      {
      const Word &w = *s.at(j);

      // teraz rozjazd: albo delta, albo jakiś operator
      QStringList args = w.getLabels();
      if (Word::WT_DELTA == w.getType())
        {
        if (args.size() > 1)
          sExp += w.getText() + "(" + args[0] + "," + args[1] + ")";
        else
          qWarning() << Q_FUNC_INFO << "Not enough labels in delta";
        }
      else if (Word::WT_BRACKET == w.getType())
        {
        const Bracket *bracket = static_cast<const Bracket*>(&w);
        sExp += "(" + expToString(*bracket) + ")";
        }
      else if (Word::WT_OPERATOR == w.getType())
        {
        sExp += w.getText() + "_";
        if (args.size() > 1)
          {          
          sExp += "{";
          for (int i = 0; i < args.size(); i++)
            {
            sExp += args.at(i);
            if (i != args.size() - 1)
              sExp += ' ';
            }
          sExp += '}';
          }
        else if (args.size() > 0)
          sExp += args[0];
        }
      else if (Word::WT_VARIABLE == w.getType())
        sExp += w.getText();
      else if (Word::WT_FUNCTION == w.getType())
        {
        sExp += w.getText() + "(";
        for (int i = 0; i < args.size(); i++)
          {
          sExp += args.at(i);
          if (i != args.size() - 1)
            sExp += ',';
          }        
        sExp += ")";
        }  
      sExp += " ";
      }
    }

  sExp.squeeze();
  return sExp.trimmed();
  }

int Translator::parseCoefficient(int i, const QString &str, double& coeff)
  {
  coeff = 1;
  if (i >= str.size())
    {
    qWarning() << Q_FUNC_INFO << " parse error!";
    return -1;
    }

  if (str.at(i) == '-')
    {
    coeff = -1;
    i++;
    }
  else if (str.at(i) == '+')
    i++;

  // fast forward any spaces, if there are any
  while ((i < str.size()) && (str.at(i) == ' '))
    i++;

  if (!QChar(str.at(i)).isDigit())
    return i;

  // read the number until we meet a nondigit, nonpoint character
  QString sNumber = "";
  while (i < str.size() && (str.at(i).isDigit() || str.at(i) == '.'))
    {
    sNumber += str.at(i);
    i++;
    }

  bool conversionSuccess;
  coeff = sNumber.toDouble(&conversionSuccess) * coeff;
  if (!conversionSuccess)
    return -1;
  
  return i;
  }

int Translator::parseBracket(int i, const QString &str, Bracket &bracket)
  {
  QString sExpInBrackets;
  int nOpenedBrackets = 1;
  i++;
  while ((nOpenedBrackets > 0) && ((i < str.size())))
    {
    if (str.at(i) == '(')
      nOpenedBrackets++;
    if (str.at(i) == ')')
      {
      nOpenedBrackets--;
      if (!nOpenedBrackets) // ostatni nawias zamknięty, nie jest on częścią expresssion więc kończymy
        break;
      }
    sExpInBrackets += str.at(i);
    i++;
    }
  i++;
  sExpInBrackets = sExpInBrackets.trimmed();
  if (stringToExp(sExpInBrackets, bracket))
    return i;
  else
    qWarning() << "stringToExp failed for " << sExpInBrackets;
  return -1;
  }

bool Translator::stringToSentence( const QString &str, Sentence &sen)
  {
  Expression exp;
  if (!stringToExp(str, exp))
    return false;
  if (exp.size() != 1)
    return false;
  else
    sen = std::move(exp.at(0));
  return true;
  }

/* Returns false if can't parse the string */
bool Translator::stringToExp( const QString &string, Expression &exp)
  {
  exp.clear();
  QString str = string.trimmed();

  ParserError error = PE_NO_ERROR;

  Sentence sentence;
  int i = 0;
  while (i < str.size())
    {
    // skip spaces
    while (str.at(i) == ' ')
      i++;

    QChar c = str.at(i);
    if (c.isNumber() || (c == '+') || (c == '-'))
     {
     bool isThatFirstChar = !bool(i);
     if (!c.isNumber() && !isThatFirstChar)
       {
       exp.push_back(std::move(sentence));
       sentence.cleanup();
       }
     double coeff;
     i = parseCoefficient(i, str, coeff);
     if (i == -1)
       {
       error = PE_COEFFICIENT;
       qWarning() << QObject::tr("Failed to parse coefficient in >%1<").arg(str);
       break;
       }
     sentence.setCoefficient(coeff);
     continue;
     }

    // bracket
    if (str.at(i) == '(')
      {
      Bracket *bracket = new Bracket();
      i = parseBracket(i, str, *bracket);
      if (i > 0)
        sentence.insert(sentence.size(), bracket);
      else
        {
        qWarning() << QObject::tr("Failed to parse bracket in >%1<").arg(str);
        error = PE_BRACKET;
        break;
        }
      continue;
      }

    // liczby i nawiasy z głowy. Teraz spodziewam się tekstu
    // czyli: zmiennej, funkcji lub operatora
    QString sName;
    while ((i < str.size()) &&
           (str.at(i) != '_') && 
           (str.at(i) != '(') &&
           (str.at(i) != ' ') )  // zbieram znaki tak długo aż tafię na _, (, lub skończy się string
      {
      sName += str.at(i);
      i++;
      }

    Word * word = 0;
    Word::Type wordType = Word::WT_INVALID;
    if ((i >= str.size()) || (str.at(i) == ' '))
      wordType = Word::WT_VARIABLE;
    else if (str.at(i) == '_') 
      {
      if (_registeredNames.value(sName) == Word::WT_OPERATOR)
        wordType = Word::WT_OPERATOR;
      else
        wordType = Word::WT_FUNCTION;
      }
    else if (str.at(i) == '(')
      {
      if (_registeredNames.value(sName) == Word::WT_DELTA)
        wordType = Word::WT_DELTA;
      else
        wordType = Word::WT_FUNCTION;
      }

    switch (wordType)
      {
      case Word::WT_VARIABLE:
        word = new Variable(sName);
        break;
      case Word::WT_DELTA:
        {
        QStringList labels;
        i = parseArguments(i, str, labels);
        if ((-1 == i) || (labels.size() != 2))
          {
          qWarning() << QObject::tr("Couldn't parse delta labels! i=%1, c=%2").arg(i).arg(str.at(i));
          error = PE_DELTA_LABELS;
          }
        else
          word = new Delta(sName, labels[0], labels[1]);
        }
        break;
      case Word::WT_OPERATOR:
        {
        QStringList labels;
        i = parseArguments(i, str, labels);
        if (-1 == i)
          {
          qWarning() << QObject::tr("Couldn't parse operator label! i=%1, opereator=%2").arg(i).arg(sName);
          error = PE_OPERATOR_LABEL;
          }
        else
          word = new Operator(sName, labels);
        }
        break;
      case Word::WT_FUNCTION:
        {
        QStringList arguments;
        i = parseArguments(i, str, arguments);
        if (-1 == i)
          {
          qWarning() << QObject::tr("Couldn't parse function arguments labels! i=%1, func_name=%2").arg(i).arg(sName);
          error = PE_FUNCTION_ARGUMENTS;
          }
        else
          word = new Function(sName, arguments);
        }
        break;
      default:
        break;
      }

    if (error)
      break;

    if (word)
      sentence.insert(sentence.size(), word);
    } // while end

  if (!error)
    exp.push_back(sentence);
  else
    {
    exp.clear();
    return false;
    }
  return true;
  }
  
int Translator::parseArguments(int i, const QString &str, QStringList &args)
  {
  int openedBrackets = 0;
  QString argument;
  argument.reserve(5);
  if (str.at(i) == '_')
    {
    if (str.at(++i) == '{')
      {
      openedBrackets++;
      while (openedBrackets > 0)
        {        
        if (i+1 == str.size())
          return -1;
        if (str.at(++i) == '}')
          {
          if (--openedBrackets)
            argument += str.at(i);          
          }
        else if (str.at(i) != ' ')
          {
          argument += str.at(i);
          if (str.at(i) == '{')
            openedBrackets++;
          }
        else
          {
          args << argument;
          argument = "";
          }
        }
      if (argument.size() > 0)
        args << argument;
      }
    else
      {
      while ((i < str.size()) && (str.at(i) != ' '))
        {
        argument += str.at(i);        
        i++;
        }
      args << argument;
      }
    }
  else if (str.at(i) == '(')
    {
    while (str.at(++i) != ')')
      {
      if (str.at(i) != ',')
        {
        if (str.at(i) != ' ')  // ignore spaces
          argument += str.at(i);
        }
      else
        {
        args << argument;
        argument = "";
        }
      if (i+1 == str.size())
        return -1;
      }
    if (argument.size() > 0)
      args << argument;
    }
  else
    return -1;

  return ++i;
  }

